
#include <SFML/System.hpp>

#include "ExceptionSafeThread.hpp"
#include "TraceQueue.hpp"

//To remove
#include <iostream>

namespace se{

/********************************************//**
 * Creates an instance of the class.
 ***********************************************/
System::Trace::TraceQueue::TraceQueue():
	m_stringsToWrite(),
	m_mutex(),
	m_lockInput(false){
}

/********************************************//**
 * Destroy the instance of the class.
 ***********************************************/
System::Trace::TraceQueue::~TraceQueue(){
	// Nothing to do.
}

/********************************************//**
 * Push a message on the queue.
 *
 * The message will be written in black.
 * \param text The message text.
 * \param flags The channels on which the message
 * will be printed.
 * \param critical Whether the message is written
 * before continuing execution. Slow down a lot if
 * true.
 ***********************************************/
void System::Trace::TraceQueue::Push(const char* text, Flags8_t flags, bool critical){
	// TODO: make a default color.
	Color_t col;
	this->Push(text, flags, col, critical);
}

/********************************************//**
 * Push a message on the queue.
 *
 * \param text The message text.
 * \param flags The channels on which the message
 * will be printed.
 * \param col The wanted color of the message, if
 * possible given the writing destination.
 * \param critical Whether the message is written
 * before continuing execution. Slow down a lot if
 * true.
 ***********************************************/
void System::Trace::TraceQueue::Push(const char* text, Flags8_t flags, Color_t col, bool critical){
	if(!m_lockInput){
		TraceOutput_t toPush;
		strncpy((char*)(&(toPush.text)),text,sizeof(toPush.text));// Why does I need a cast ?
		toPush.channels = flags;
		toPush.color = col;
		toPush.critical = critical;
		if(!m_lockInput){
			boost::unique_lock<boost::mutex> lk(m_mutex);
			m_stringsToWrite.push_back(toPush);
		}
		if(critical){// wait for the queue to be empty. 
			boost::unique_lock<boost::mutex> lk(m_mutex);
			while(!m_stringsToWrite.empty()) m_queueUpdated.wait(lk);
		}
	}
}

/********************************************//**
 * Returns a pointer to the first pushed item.
 * 
 * Note that internally, this is the actual item
 * that is stored, so don't pop it before you are
 * finished.
 * 
 * In normal conditions (finish==false), this method
 * always returns an item, and block until there is one.
 *
 * \param finish This must be false by default,
 * and externally set to true (ie: not in this thread)
 * when an exit is wished. It is present to allow the
 * queue to continue to empty if not, but also to
 * leave the blocking state if needed while the
 * queue is empty.
 * \return The adress of the first pushed item,
 * or NULL if the queue is empty and that finish is true.
 ***********************************************/
/// Is assumed that there is no more than 1 writing thread. If not, behaviour undefined.
/// There is always a return to this function. By consequence, is blocking.
System::Trace::TraceOutput_t* System::Trace::TraceQueue::GetFront(ThreadSignal_t& finish){
	TraceOutput_t* ret;
	// TODO : Repace it with a event based solution ( c++11 ).
	while(m_stringsToWrite.empty() && !finish) sf::sleep(sf::milliseconds(10));
	if(finish && m_stringsToWrite.empty()) return NULL;
	{boost::unique_lock<boost::mutex> lk(m_mutex);
		ret = &(m_stringsToWrite.front());
	}
	return ret;
}

/********************************************//**
 * Remove the first item pushed.
 ***********************************************/
void System::Trace::TraceQueue::PopFront(){
	boost::unique_lock<boost::mutex> lk(m_mutex);
	m_stringsToWrite.pop_front();
	m_queueUpdated.notify_one();
}
/********************************************//**
 * Lock the input.
 *
 * When the trace attempt to close, it will call
 * this function, which forbids any additionnals
 * calls to Push.
 *
 * This will cause any call to Push to do nothing,
 * but those already into the function will write
 * their message anyway.
 *
 * This cannot be reversed.
 ***********************************************/
void System::Trace::TraceQueue::LockInput(){
	m_lockInput=true;
}
}// namespace se
